package com.quectel.business.pay.handler;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.google.common.base.Preconditions;
import com.quectel.business.common.parking.ParkingBusiness;
import com.quectel.business.common.parking.ParkingGateCmdBusiness;
import com.quectel.business.pay.handler.model.CreateBusinessResp;
import com.quectel.business.pay.vo.CreateOrderRequest;
import com.quectel.constant.core.parking.ParkingOrderConstants;
import com.quectel.core.module.parking.dto.ParkingChargingDto;
import com.quectel.core.module.parking.dto.ParkingOrderDto;
import com.quectel.core.module.parking.service.ParkingChargingService;
import com.quectel.core.module.parking.service.ParkingOrderService;
import com.quectel.core.module.parkingcard.dto.ParkingCardMemberDto;
import com.quectel.core.module.parkingcard.service.ParkingCardMemberService;
import com.quectel.core.module.village.dto.VillageDto;
import com.quectel.core.module.village.service.VillageService;
import com.quectel.pay.module.order.dto.PayOrderDto;
import com.quectel.pay.module.order.dto.PayOrderRefundDto;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.common.Response;
import com.quectel.util.redis.RedisUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.function.Function;

@Slf4j
@Component
public class ParkingFeePayHandler implements BasePayHandler {

    /**
     * 订单有效支付时间
     */
    private static final long TTL = 1L * 60 * 1000;
    /**
     * 下一次安全时间间隔
     */
    private static final long SECURITY_TIME = TTL + 1L * 60 * 1000;

    private static final Function<Long, String> PARKING_ORDER_LOCK_FUNC = orderId -> "PARKING_ORDER_LOCK:" + orderId;

    @DubboReference
    private ParkingOrderService parkingOrderService;
    @DubboReference
    private VillageService villageService;
    @DubboReference
    private ParkingChargingService parkingChargingService;
    @DubboReference
    private ParkingCardMemberService parkingCardMemberService;
    @Autowired
    private ParkingBusiness parkingBusiness;
    @Autowired
    private ParkingGateCmdBusiness parkingGateCmdBusiness;

    /**
     * 临停缴费参数
     */
    @Data
    private static class ParkingFeeParamsCls{
        private Long parkingOrderId;
    }

    @Override
    public Response<CreateBusinessResp> createBusinessOrder(CreateOrderRequest req) {
        ParkingFeePayHandler.ParkingFeeParamsCls parkingFeeParamsCls = BeanUtil.toBean(req.getOtherBusinessParams(), ParkingFeePayHandler.ParkingFeeParamsCls.class);
        Long parkingOrderId = parkingFeeParamsCls.getParkingOrderId();

        Lock spinLock = RedisUtils.getSpinLock(PARKING_ORDER_LOCK_FUNC.apply(parkingOrderId));
        spinLock.lock();


        try {
            final Date currentDate = new Date();

            ParkingOrderDto parkingOrderDto = parkingOrderService.selectById(parkingOrderId);
            if (Objects.isNull(parkingOrderDto)) {
                parkingOrderDto = parkingOrderService.selectByPreId(parkingOrderId);
                if (Objects.isNull(parkingOrderDto)) {
                    return Response.error("停车订单不存在");
                }
            }
            ParkingCardMemberDto memberDto = parkingCardMemberService.selectValidMember(parkingOrderDto.getVillageId()
                    , parkingOrderDto.getLicense(), parkingOrderDto.getLicenseColor());
            if (Objects.nonNull(memberDto)) {
                return Response.error("会员无需支付");
            }
            if (!ParkingOrderConstants.OrderPayStatusEnum.NO_PAY.getPayStatus().equals(parkingOrderDto.getPayStatus())) {
                return Response.error("订单被支付，无需重复支付");
            }
            if (StringUtils.isNotBlank(parkingOrderDto.getBuyerId())
                    && !req.getBuyerId().equals(parkingOrderDto.getBuyerId())
                    && parkingOrderDto.getPayTime() != null
                    && currentDate.getTime() - parkingOrderDto.getPayTime().getTime() < SECURITY_TIME) {
                return Response.error("订单正在被支付，请稍后再试");
            }

            VillageDto villageDto = villageService.selectCacheById(parkingOrderDto.getVillageId());
            Preconditions.checkNotNull(villageDto);

            // 有压线时间，优先采用压线时间计算费用，有压线时间即非预支付，需要判断压线时间与当前时间间隔(大于设定值不允许支付)

            Date crimpingTime = parkingOrderDto.getCrimpingTime();
            if (Objects.nonNull(crimpingTime) && DateUtil.between(crimpingTime, currentDate, DateUnit.MINUTE, Boolean.FALSE)
                    > ParkingOrderConstants.TRIGGER_MINUTES_SPACE_LIMIT) {
                return Response.error("压线时间异常，请尝试重新压线后支付");
            }

            ParkingChargingDto parkingChargingDto = JacksonUtils.parse(parkingOrderDto.getChargingConfBak(), ParkingChargingDto.class);
            BigDecimal payAmount = parkingBusiness.calAmount(parkingChargingDto, parkingOrderDto.getInTime()
                    , Objects.nonNull(crimpingTime) ? crimpingTime : currentDate);

            ParkingOrderDto newlyDto = new ParkingOrderDto();
            newlyDto.setId(parkingOrderDto.getId());
            newlyDto.setDueAmount(payAmount);
            newlyDto.setBuyerId(req.getBuyerId());
            newlyDto.setMonthCardDiscount(BigDecimal.ZERO);
            newlyDto.setPayType(req.getPayType());
            newlyDto.setPayAmount(payAmount);
            newlyDto.setPayTime(currentDate);
            newlyDto.setChargingConfId(parkingChargingDto.getId());
            newlyDto.setChargingConfBak(JacksonUtils.toJsonString(parkingChargingDto));
            parkingOrderService.updateById(newlyDto);

            CreateBusinessResp createBusinessResp = new CreateBusinessResp();
            createBusinessResp.setTenantId(parkingOrderDto.getTenantId());
            createBusinessResp.setVillageId(parkingOrderDto.getVillageId());
            createBusinessResp.setBusinessId(parkingOrderDto.getId());
            createBusinessResp.setProductName(String.format("%s_%s停车收费", villageDto.getName(), parkingOrderDto.getLicense()));
            createBusinessResp.setOriginalPrice(payAmount);
            createBusinessResp.setSpecialPrice(payAmount);
            createBusinessResp.setTtl(TTL);
            return Response.<CreateBusinessResp>ok().wrap(createBusinessResp);
        } catch (Exception e) {
            log.error("ParkingFeePayHandler createBusinessOrder error", e);
        } finally {
            spinLock.unlock();
        }

        return Response.error("停车订单创建失败");
    }

    @Override
    public Response<Object> createOrderSuccessNotify(PayOrderDto notifyOrder) {

        ParkingOrderDto parkingOrderDto = parkingOrderService.selectCacheById(notifyOrder.getBusinessId());
        Preconditions.checkNotNull(parkingOrderDto);

        ParkingOrderDto newlyDto = new ParkingOrderDto();
        newlyDto.setId(parkingOrderDto.getId());
        newlyDto.setOrderId(notifyOrder.getId());
        newlyDto.setOrderNo(notifyOrder.getOutTradeNo());
        parkingOrderService.updateById(newlyDto);
        return Response.ok();
    }

    @Override
    public Response<Object> createOrderFailedNotify(CreateOrderRequest req) {
        return null;
    }

    @Override
    public Response<Object> cancelOrder(PayOrderDto cancelOrder) {
        return Response.ok();
    }

    @Override
    public Response<Object> paySuccessNotify(PayOrderDto order) {
        ParkingOrderDto parkingOrderDto = parkingOrderService.selectById(order.getBusinessId());
        Preconditions.checkNotNull(parkingOrderDto);

        ParkingOrderDto newlyDto = new ParkingOrderDto();
        newlyDto.setId(parkingOrderDto.getId());
        newlyDto.setIsAdvance(Objects.nonNull(parkingOrderDto.getCrimpingTime()) ? ParkingOrderConstants.AdvanceEnum.NO_ADVANCE.getAdvanceType()
                : ParkingOrderConstants.AdvanceEnum.PAY_ADVANCE.getAdvanceType());
        newlyDto.setOrderId(order.getId());
        newlyDto.setPayStatus(ParkingOrderConstants.OrderPayStatusEnum.PAY_SUCCESS.getPayStatus());
        newlyDto.setOrderNo(order.getOutTradeNo());
        parkingOrderService.updateById(newlyDto);

        if (ParkingOrderConstants.AdvanceEnum.NO_ADVANCE.getAdvanceType().equals(parkingOrderDto.getIsAdvance())
                && ParkingOrderConstants.GuardOpenStatusEnum.NEVER_OPENED.getOpenStatus().equals(parkingOrderDto.getOpenGuardStatus())) {
            // 闸从未开启，压线支付成功，下发开闸指令
            parkingGateCmdBusiness.open(parkingOrderDto.getCrimpingDeviceId(), parkingOrderDto.getId());
        }
        return Response.ok();
    }

    @Override
    public Response<Object> refundPreCheck(PayOrderDto payOrderDto, BigDecimal refundAmount) {
        return Response.ok();
    }

    @Override
    public Response<Object> refundSuccessNotify(PayOrderRefundDto refundOrder) {
        return null;
    }
}
